package com.xjuse.yida.commom.minio.minio;

import cn.hutool.core.io.IoUtil;
import com.xjuse.yida.commom.minio.config.MinioProperties;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.XmlParserException;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static java.lang.System.in;

/***
 * @Description minio文件服务器 操作服务类
 * @Date 16:37 2022/10/23
 * @author 嗜雪的蚂蚁
 **/
@Component
@Slf4j
@RequiredArgsConstructor
public class MinioHandler {

    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioProperties minioProperties;

    /**
     * 判断bucket是否存在
     *
     * @param name 存储桶名称
     */
    public boolean existBucket(String name) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     */
    public boolean createdBucket(String bucketName) throws Exception {
        if (!this.existBucket(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            return true;
        }
        return false;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储bucket名称
     */
    public boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * description: 上传文件
     *
     * @param file 文件
     * @return: List<String> s
     */
    public String saveFile(MultipartFile file) throws Exception {
        if (Objects.isNull(file)) {
            return "";
        }
        String fileName;
        try {
            fileName = file.getOriginalFilename();
            InputStream in = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(fileName)
                    .stream(in, in.available(), -1)
                    .contentType(file.getContentType())
                    .build()
            );
        } finally {
            Optional.ofNullable(in).ifPresent(inputStream -> {
                try {
                    inputStream.close();
                } catch (IOException ignored) {
                }
            });
        }
        log.info("文件保存成功,文件名：{},类型：{}", fileName, file.getContentType());
        return fileName;
    }

    /**
     * description: 上传文件
     *
     * @param files 文件
     * @return: List<String> s  返回上传后的文件名列表
     */
    public List<String> saveFile(MultipartFile[] files) throws Exception {
        List<String> names = new ArrayList<>(files.length);
        for (MultipartFile file : files) {
            names.add(this.saveFile(file));
        }
        return names;
    }

    /**
     * 获取文件
     */
    public InputStream getFile(String fileName) throws Exception {
        return minioClient.getObject(GetObjectArgs.builder().bucket(minioProperties.getBucketName()).object(fileName).build());
    }

    /**
     * description: 下载文件
     *
     * @param fileName 文件名
     * @return: org.springframework.http.ResponseEntity<byte [ ]>
     */
    public ResponseEntity<byte[]> download(String fileName) {
        return this.download(fileName, MediaType.APPLICATION_OCTET_STREAM);
    }

    /**
     * description: 下载文件
     *
     * @param fileName 文件名
     * @return: org.springframework.http.ResponseEntity<byte [ ]>
     */
    public ResponseEntity<byte[]> download(String fileName, MediaType mediaType) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        try {
            in = this.getFile(fileName);
            // 封装返回值
            byte[] bytes = IoUtil.readBytes(in);
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, String.valueOf(StandardCharsets.UTF_8)));
            headers.setContentLength(bytes.length);
            headers.setContentType(mediaType);
            headers.setAccessControlExposeHeaders(Collections.singletonList("*"));
            responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Optional.ofNullable(in).ifPresent(
                    i -> {
                        try {
                            i.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
            );
        }

        return responseEntity;
    }

    /**
     * 查看文件对象
     *
     * @param bucketName 存储bucket名称
     */
    public void listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                String s = item.objectName();
                long size = item.size();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 批量删除文件对象
     *
     * @param bucketName 存储bucket名称
     * @param objects    对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(DeleteObject::new).collect(Collectors.toList());
        return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
    }

    /**
     * 上传后生成的URL的过期时间
     **/
    private static final int DEFAULT_EXPIRY_TIME = 7;

    /**
     * 生成一个给HTTP GET请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载，即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间，默认值是7天。
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param expires    失效时间（以秒为单位），默认是7天，不得大于七天
     * @return
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception {
        boolean flag = existBucket(bucketName);
        String url = "";
        if (flag) {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new Exception("可设置的过期时间为:1~" + DEFAULT_EXPIRY_TIME + "天");
            }
            try {
                url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expires * 24 * 3600)//动态参数
//                        .expiry(24 * 60 * 60)//用秒来计算一天时间有效期
//                        .expiry(1, TimeUnit.DAYS)//按天传参
//                        .expiry(1, TimeUnit.HOURS)//按小时传参数
                        .build());
            } catch (Exception e) {
                log.info("生成对象访问URL时异常:bucketName={},objectName={},expires={}", bucketName, objectName, expires);
            }
        }
        return url;
    }

    /**
     * 生成一个给HTTP PUT请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行上传，即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间，默认值是7天。
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @param expires    失效时间（以秒为单位），默认是7天，不得大于七天
     * @return
     */
    public String presignedPutObject(String bucketName, String objectName, Integer expires) throws Exception {
        boolean flag = existBucket(bucketName);
        String url = "";
        if (flag) {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new Exception("可设置的过期时间为:1~" + DEFAULT_EXPIRY_TIME + "天");
            }
            try {
                url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(Method.PUT)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expires)//动态参数
//                        .expiry(24 * 60 * 60)//用秒来计算一天时间有效期
//                        .expiry(1, TimeUnit.DAYS)//按天传参
//                        .expiry(1, TimeUnit.HOURS)//按小时传参数
                        .build());
            } catch (Exception e) {
                log.info("生成对象访问URL时异常:bucketName={},objectName={},expires={}", bucketName, objectName, expires);
            }
        }
        return url;
    }
}
